using System;

namespace RFS.BizLogic.utils
{
    
    

public   class SnowflakeIdGenerator
{
    private const long Twepoch = 1609459200000L; // 2021-01-01 00:00:00 UTC
    private const int WorkerIdBits = 10;
    private const int DatacenterIdBits = 5;
    private const int SequenceBits = 12;

    private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
    private const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits);
    private const int WorkerIdShift = SequenceBits;
    private const int DatacenterIdShift = SequenceBits + WorkerIdBits;
    private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
    private const long SequenceMask = -1L ^ (-1L << SequenceBits);

    private long _sequence = 0L;
    private long _lastTimestamp = -1L;

    private readonly long _workerId;
    private readonly long _datacenterId;
    private readonly object _lock = new object();

    public SnowflakeIdGenerator(long workerId=123, long datacenterId=3)
    {
        if (workerId < 0 || workerId > MaxWorkerId)
            throw new ArgumentException($"workerId must be between 0 and {MaxWorkerId}");

        if (datacenterId < 0 || datacenterId > MaxDatacenterId)
            throw new ArgumentException($"datacenterId must be between 0 and {MaxDatacenterId}");

        _workerId = workerId;
        _datacenterId = datacenterId;
    }

    public ulong NextId()
    {
        lock (_lock)
        {
            long timestamp = TimeGen();

            if (timestamp < _lastTimestamp)
            {
                throw new Exception($"Clock moved backwards. Refusing to generate id for {_lastTimestamp - timestamp} milliseconds");
            }

            if (_lastTimestamp == timestamp)
            {
                _sequence = (_sequence + 1) & SequenceMask;
                if (_sequence == 0)
                {
                    timestamp = TilNextMillis(_lastTimestamp);
                }
            }
            else
            {
                _sequence = 0;
            }

            _lastTimestamp = timestamp;

            // Ensure the timestamp part is non-negative
            long adjustedTimestamp = timestamp - Twepoch;
            if (adjustedTimestamp < 0)
            {
                throw new Exception("Adjusted timestamp is negative. Check your Twepoch value.");
            }

            // Combine all parts into a ulong
            return (ulong)((adjustedTimestamp << TimestampLeftShift) |
                           (_datacenterId << DatacenterIdShift) |
                           (_workerId << WorkerIdShift) |
                           _sequence);
        }
    }

    private long TilNextMillis(long lastTimestamp)
    {
        long timestamp = TimeGen();
        while (timestamp <= lastTimestamp)
        {
            timestamp = TimeGen();
        }
        return timestamp;
    }

    private long TimeGen()
    {
        return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
    }
}
}